<?php
/**
 * dbfront
 * (c) 2010-2013 Stephen Adkins <spadkins@gmail.com>
 * This software began development in 2010.
 * It is based on code dating from 1999.
 *
 * License: GPL 2.0
 * The contents of this file may be used under the terms of
 * the GNU General Public License Version 2 or later (the "GPL").
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 * */

/**
 * FILE: extjs-store.php
 * This file is designed as the handler for all Ajax calls to support the ExtJS 4.1.X Ext.Grid.GridPanel component.
 * The following are the parameters recognized.
 *     u               - the username                     (required on all queries)  **FUTURE/NOT YET IMPLEMENTED**
 *     p               - the password                     (required on all queries)  **FUTURE/NOT YET IMPLEMENTED**
 *     op              - 'get' or 'put'                   (required on all queries)
 *     type            - the schema                       (required on all queries)
 *     key             - the table to operate on          (required on all queries)
 *     data            - comma-separated list of columns  (required on 'put' queries)
 *     neat            - used for debugging, this puts extra newlines
 * */

header('Content-type: text/plain');

require_once('frontstart.inc.php');   # defines: $options[], $context

class Application {
    function handle_request ($options) {
        try {
            $context = App::context($options);
            global $trace, $debug_sql;
            $test_mode = isset($options['test_mode']) ? $options['test_mode'] : 0;
            $debug_sql = ($test_mode && isset($_REQUEST['debug_sql']) && $_REQUEST['debug_sql']) ? 1 : 0;
            $trace     = ($test_mode && isset($_REQUEST['trace']) && $_REQUEST['trace']) ? 1 : 0;

            if (! isset($_REQUEST['op'])) throw new \Exception('ERROR: the "op" parameter must be supplied');
            $op = $_REQUEST['op'];
            if (! preg_match('/^(put|get|delete)$/', $op)) throw new \Exception('ERROR: the "op" parameter is unknown (op must be "put", "get", or "delete")');

            if (! isset($_REQUEST['type'])) throw new \Exception('ERROR: the "type" parameter must be supplied');
            $type = $_REQUEST['type'];
            if (! preg_match('/^[A-Za-z0-9_]+$/', $type)) throw new \Exception('ERROR: the "type" parameter has illegal characters (type may only be the [A-Za-z0-9_] characters)');

            if (! isset($_REQUEST['key'])) throw new \Exception('ERROR: the "key" parameter must be supplied');
            $key = $_REQUEST['key'];
            if ($op != 'delete' && ! preg_match('/^[ -&\\(-\\[\\]-~]+$/', $key)) throw new \Exception('ERROR: the "key" parameter has illegal characters (key may only be the printable ASCII characters excluding [\'"\\])');

            $label     = (isset($_REQUEST['label'])   && $_REQUEST['label'])   ? $_REQUEST['label'] : "";

            if ($op === 'put') {
                if (! isset($_REQUEST['data'])) throw new \Exception('ERROR: the "data" parameter must be supplied');
                $data = $_REQUEST['data'];
            }

            $neat    = (isset($_REQUEST['neat'])    && $_REQUEST['neat'])    ? 1 : 0;

            $results = Array();
            $results['success'] = true;   # assume we are going to succeed, whatever it is we are doing

            ###################################################################################################
            # Perform Actions
            ###################################################################################################

            $db = $context->database("auth");

            $matches = Array();

            if (preg_match('/^([a-zA-Z0-9_]+)\\.([a-zA-Z0-9_]+)\\.([a-zA-Z0-9_]+)\\.(.+)$/', $key, $matches)) {
                $base_key = "$matches[1].$matches[2].$matches[3]";
            }
            elseif (preg_match('/^([a-zA-Z0-9_]+)\\.([a-zA-Z0-9_]+)\\.([a-zA-Z0-9_]+)$/', $key, $matches)) {
                $base_key = $key;
            }
            elseif ($op != 'delete') {
                throw new \Exception('ERROR: the "key" parameter [$key] has the wrong structure ("{database}.{schema}.{table}" or "{database}.{schema}.{table}.{view})"');
            }

            if ($op === 'put') {
                if ($label) {
                    $view_part = strtolower($label);
                    $view_part = preg_replace('/[^a-z0-9]+/','_',$view_part);
                    $key = "$base_key.$view_part";
                    if ($op === 'put') {
                        $update_columns = Array('data', 'content_str');
                        $insert_columns = Array('store_type', 'store_key', 'data', 'content_str');
                        $update_values  = Array($data, $label);
                        $insert_values  = Array($type, $key, $data, $label);
                    }
                }
                else {  # If no label, not a "Save As". Leave the $key alone. (unchanged)
                    if ($op === 'put') {
                        $update_columns = Array('data');
                        $insert_columns = Array('store_type', 'store_key', 'data');
                        $update_values  = Array($data);
                        $insert_values  = Array($type, $key, $data);
                    }
                }
                if ($type === 'grid') {
                    $data = str_replace('\\"','"',$data);
                    $griddata = json_decode($data, true);
                    if (isset($griddata) && isset($griddata['sql'])) {
                        $sql = $griddata['sql'];
                        if (!preg_match('/^ *select/i', $sql))
                            throw new \Exception('ERROR: saved SQL must begin with the "select" keyword');
                        if (preg_match('/^([A-Za-z0-9_]+)/', $key, $matches)) {
                            $database = $matches[1];
                            $context->database($database)->get_rows($sql);
                        }
                    }
                }

                $nrows = $db->update('app_store', Array('store_type-eq'=>$type, 'store_key-eq'=>$key), $update_columns, $update_values);
                if ($nrows == 0) {
                    $nrows = $db->insert('app_store', $insert_columns, $insert_values);
                    $results['message'] = "$nrows row(s) inserted";
                }
                else {
                    $results['message'] = "$nrows row(s) updated";
                }
                $json = json_encode($results);

                if ($neat) {
                    $json = preg_replace('/\\},\\{/', "},\n {", $json);
                    $json = preg_replace('/\\],\\[/', "],\n [", $json);
                }

                echo $json . "\n";
            }
            elseif ($op === 'get') {
                echo $db->get('app_store', Array('store_type-eq'=>$type, 'store_key-eq'=>$key), 'data');
            }
            elseif ($op === 'delete') {
                if (!$test_mode) throw new \Exception('ERROR: the "delete" op is not allowed except if the "test_mode" is set in "options.ini"');
                $nrows = $db->execute("delete from app_store where store_type = '$type' and store_key like '$key'");
                $results['message'] = "$nrows row(s) deleted";
                $json = json_encode($results);
                echo $json . "\n";
            }
            else {
                throw new \Exception('ERROR: the "op" parameter is unknown [$op]');
            }

        }
        catch (Exception $e) {
            $this->return_json_error($e->getMessage());
        }
        return(null);
    }

    function return_json_error ($message) {
        if (preg_match("/\n/",$message)) {
            $message = preg_replace("/ *\n */"," ",$message);
        }
        if (preg_match("/\\'/",$message)) {
            $message = preg_replace("/\\'/","\\'",$message);
        }
        echo "{\n";
        echo "    'success': false,\n";
        echo "    'msg': '$message'\n";
        echo "}\n";
    }
}

$application = new Application();
$application->handle_request($options);

